import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { Button, Form, Input, Space } from 'antd';
import { FormTextFieldValidator, FormTextFieldValidatorArguments } from 'my-web-core/validators';
import { useEffect, useRef, useState } from 'react';

import AuthService from '@/features/authentication/services/auth-service';
import UsersService from '@/features/users/services/users-service';
import FormItemHelper, { FormItemValidationResultMapping } from '@/shared/models/form-item-helper';

import '@/features/users/components/EditUser.css';

function EditUser(props: { id?: string | null; cancel: () => void; ok: () => void }) {
  // For validation purpose.
  const validationResultMappingDefaultValue: FormItemValidationResultMapping = {
    username: {
      name: 'username',
      label: '用户名',
    },
    password: {
      name: 'password',
      label: '密码',
      validatorArguments: {
        minLength: 6,
      },
    },
    confirmPassword: {
      name: 'confirmPassword',
      label: '确认密码',
    },
  };

  const [validationResultMapping, setValidationResultMapping] =
    useState<FormItemValidationResultMapping>(validationResultMappingDefaultValue);

  // For validating confirmPassword.
  const usernameInput = useRef<any>(null);
  const [form] = Form.useForm();

  /**
   * Performs validation on form item. It includes password & confirmPassword match check.
   */
  function validateFormItem(
    key: string,
    value: string,
    validatorArguments?: FormTextFieldValidatorArguments,
  ): void {
    // Updates form data for validating confirmPassword.
    validationResultMapping[key].value = value;

    const validator = new FormTextFieldValidator(validatorArguments);

    if (key === validationResultMapping.password.name) {
      validator.args.label = validationResultMapping.password.label!;
      validator.args.value = validationResultMapping.password.value;
      validator.args.label2 = validationResultMapping.confirmPassword.label!;
      validator.args.value2 = validationResultMapping.confirmPassword.value;
    } else if (key === validationResultMapping.confirmPassword.name) {
      validator.args.label = validationResultMapping.confirmPassword.label!;
      validator.args.value = validationResultMapping.confirmPassword.value;
      validator.args.label2 = validationResultMapping.password.label!;
      validator.args.value2 = validationResultMapping.password.value;
    } else {
      validator.args.label = validationResultMapping[key].label!;
      validator.args.value = value;
    }

    // Merges object changes.
    Object.assign(validationResultMapping[key], FormItemHelper.validate(validator));

    // If password & confirmPassword meet the validation rules and their values are identical then we need to set both status to success.
    if (
      new FormTextFieldValidator({
        ...validationResultMapping.password.validatorArguments,
        label: validationResultMapping.password.label,
        value: validationResultMapping.password.value,
      }).validate() === null &&
      new FormTextFieldValidator({
        ...validationResultMapping.confirmPassword.validatorArguments,
        label: validationResultMapping.confirmPassword.label,
        value: validationResultMapping.confirmPassword.value,
      }).validate() === null &&
      validationResultMapping.password.value === validationResultMapping.confirmPassword.value
    ) {
      validationResultMapping.password = Object.assign(
        validationResultMapping.password,
        FormItemHelper.getSuccessValidationResult(),
      );
      validationResultMapping.confirmPassword = Object.assign(
        validationResultMapping.confirmPassword,
        FormItemHelper.getSuccessValidationResult(),
      );
    }
  }

  /**
   * Handles input value change for validation purpose.
   */
  function handleChange(e: any): void {
    const key = e.target.id;
    const value = e.target.value;

    validateFormItem(key, value, validationResultMapping[key].validatorArguments);

    // To make setState work we have to create a new object with spreaded properties.
    setValidationResultMapping({ ...validationResultMapping });
  }

  /**
   * When form submitted.
   */
  async function onFinish(values: any): Promise<void> {
    for (const key in validationResultMapping) {
      validateFormItem(
        key,
        values[validationResultMapping[key].name!],
        validationResultMapping[key].validatorArguments,
      );
    }

    // To make setState work we have to create a new object with spreaded properties.
    setValidationResultMapping({ ...validationResultMapping });

    if (FormItemHelper.isFormValidationSuccessful(validationResultMapping)) {
      const usernameExists = await new AuthService().checkOnUsername(values.username);

      if (!usernameExists) {
        await new UsersService().add({
          username: values.username,
          fullName: values.username,
          password: values.password,
        });

        props.ok();

        // Resets form values.
        form.resetFields();
      } else {
        validationResultMapping.username = Object.assign(
          validationResultMapping.username,
          FormItemHelper.getErrorValidationResult('该用户名已被占用，请重新输入。'),
        );

        // To make setState work we have to create a new object with spreaded properties.
        setValidationResultMapping({ ...validationResultMapping });
      }
    }
  }

  function onCancel(): void {
    // Closes model.
    props.cancel();

    // Resets validation status.
    setValidationResultMapping(validationResultMappingDefaultValue);

    // Resets form values.
    form.resetFields();
  }

  useEffect(() => usernameInput!.current!.focus(), []);

  return (
    <Form form={form} onFinish={onFinish}>
      <Form.Item
        name={validationResultMapping.username.name}
        validateStatus={validationResultMapping.username.validateStatus}
        help={validationResultMapping.username.errorMsg}
      >
        <Input
          prefix={<UserOutlined className="site-form-item-icon" />}
          ref={usernameInput}
          placeholder={validationResultMapping.username.label}
          onChange={handleChange}
        />
      </Form.Item>
      <Form.Item
        name={validationResultMapping.password.name}
        validateStatus={validationResultMapping.password.validateStatus}
        help={validationResultMapping.password.errorMsg}
      >
        <Input
          prefix={<LockOutlined className="site-form-item-icon" />}
          type="password"
          placeholder={validationResultMapping.password.label}
          onChange={handleChange}
        />
      </Form.Item>
      <Form.Item
        name={validationResultMapping.confirmPassword.name}
        validateStatus={validationResultMapping.confirmPassword.validateStatus}
        help={validationResultMapping.confirmPassword.errorMsg}
      >
        <Input
          prefix={<LockOutlined className="site-form-item-icon" />}
          type="password"
          placeholder={validationResultMapping.confirmPassword.label}
          onChange={handleChange}
        />
      </Form.Item>
      <Form.Item className="EditUser-button-container">
        <Space>
          <Button onClick={onCancel}>取消</Button>
          <Button type="primary" htmlType="submit">
            保存
          </Button>
        </Space>
      </Form.Item>
    </Form>
  );
}

export default EditUser;
